package com.ciccwm.auth.strategy.impl;

import com.ciccwm.auth.config.properties.AuthProperties;
import com.ciccwm.auth.model.OAuth2TokenResponse;
import com.ciccwm.auth.strategy.AuthenticationStrategy;
import com.ciccwm.auth.util.JwtTokenUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2Token;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 密码认证策略实现.
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class PasswordAuthenticationStrategy implements AuthenticationStrategy {
    
    private final AuthenticationManager authenticationManager;
    private final RegisteredClientRepository registeredClientRepository;
    private final OAuth2AuthorizationService authorizationService;
    private final OAuth2TokenGenerator<Jwt> tokenGenerator;
    private final JwtTokenUtil jwtTokenUtil;
    private final AuthProperties authProperties;
    
    /**
     * 获取认证类型.
     *
     * @return 返回"password"表示密码认证
     */
    @Override
    public String getType() {
        return "password";
    }
    
    /**
     * 执行密码认证.
     *
     * @param params 认证参数
     * @return OAuth2令牌响应
     * @throws IllegalArgumentException 如果参数类型不正确
     * @throws RuntimeException 如果客户端验证失败
     */
    @Override
    public OAuth2TokenResponse authenticate(AuthenticationParams params) {
        if (!(params instanceof PasswordParams)) {
            throw new IllegalArgumentException("Invalid authentication parameters");
        }
        
        PasswordParams passwordParams = (PasswordParams) params;
        log.info("Password authentication for user: {}", passwordParams.getUsername());
        
        // 验证客户端
        RegisteredClient registeredClient = validateClient(
            passwordParams.getClientId(), 
            passwordParams.getClientSecret()
        );
        
        // 验证用户
        Authentication authentication = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(
                passwordParams.getUsername(), 
                passwordParams.getPassword()
            )
        );
        
        // 创建令牌上下文
        OAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder()
            .registeredClient(registeredClient)
            .principal(authentication)
            .authorizationGrantType(new AuthorizationGrantType("password"))
            .authorizedScopes(registeredClient.getScopes())
            .authorizationGrant(authentication)
            .build();
        
        // 生成令牌
        OAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);
        if (generatedAccessToken == null) {
            throw new RuntimeException("Failed to generate access token");
        }
        
        // 创建访问令牌
        OAuth2AccessToken accessToken = new OAuth2AccessToken(
            OAuth2AccessToken.TokenType.BEARER,
            ((Jwt) generatedAccessToken).getTokenValue(),
            ((Jwt) generatedAccessToken).getIssuedAt(),
            ((Jwt) generatedAccessToken).getExpiresAt(),
            registeredClient.getScopes()
        );
        
        // 保存授权信息
        OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient)
            .principalName(authentication.getName())
            .authorizationGrantType(new AuthorizationGrantType("password"))
            .accessToken(accessToken)
            .attribute("scope", registeredClient.getScopes());
        
        OAuth2Authorization authorization = authorizationBuilder.build();
        this.authorizationService.save(authorization);
        
        return new OAuth2TokenResponse(
            accessToken.getTokenValue(),
            null,
            accessToken.getExpiresAt().getEpochSecond() - Instant.now().getEpochSecond(),
            String.join(" ", registeredClient.getScopes())
        );
    }
    
    /**
     * 验证认证参数是否有效.
     *
     * @param params 认证参数
     * @return 如果参数有效返回true，否则返回false
     */
    @Override
    public boolean validate(AuthenticationParams params) {
        if (!(params instanceof PasswordParams)) {
            return false;
        }
        
        PasswordParams passwordParams = (PasswordParams) params;
        return passwordParams.getUsername() != null && !passwordParams.getUsername().isEmpty()
            && passwordParams.getPassword() != null && !passwordParams.getPassword().isEmpty()
            && passwordParams.getClientId() != null && !passwordParams.getClientId().isEmpty()
            && passwordParams.getClientSecret() != null && !passwordParams.getClientSecret().isEmpty();
    }
    
    /**
     * 验证客户端凭证.
     *
     * @param clientId 客户端ID
     * @param clientSecret 客户端密钥
     * @return 注册的客户端
     * @throws RuntimeException 如果客户端验证失败
     */
    private RegisteredClient validateClient(String clientId, String clientSecret) {
        RegisteredClient registeredClient = registeredClientRepository.findByClientId(clientId);
        if (registeredClient == null || !registeredClient.getClientSecret().equals(clientSecret)) {
            throw new RuntimeException("Invalid client credentials");
        }
        return registeredClient;
    }
    
    /**
     * 密码认证参数.
     * 包含用户名、密码、客户端ID和客户端密钥.
     */
    @lombok.Data
    public static class PasswordParams implements AuthenticationParams {
        /**
         * 用户名.
         */
        private String username;

        /**
         * 密码.
         */
        private String password;

        /**
         * 客户端ID.
         */
        private String clientId;

        /**
         * 客户端密钥.
         */
        private String clientSecret;
        
        /**
         * 获取认证类型.
         *
         * @return 返回"password"表示密码认证
         */
        @Override
        public String getType() {
            return "password";
        }
    }
} 
